﻿using System;
using System.Windows;
using System.Windows.Data;

namespace MVVMFramework.View
{
    public abstract class ConverterBase : IValueConverter
    {
        /// <summary>
        /// Gets or sets a value indicating whether the return values should be reversed.
        /// </summary>
        public bool IsReversed
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the value to use when the converter is unable to return a value.
        /// </summary>
        public object FallbackValue
        {
            get;
            set;
        }

        public FallbackBehavior FallbackBehavior
        {
            get;
            set;
        }

        public virtual object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            try
            {
                object convertedValue = TryConvert(value, targetType, parameter, culture);
                return ReturnValue(convertedValue, targetType);
            }
            catch (Exception ex)
            {
                return ReturnFallbackValue(value, targetType, ex);
            }
        }

        public virtual object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            try
            {
                object convertedValue = TryConvertBack(value, targetType, parameter, culture);
                return ReturnValue(convertedValue, targetType);
            }
            catch (Exception ex)
            {
                return ReturnFallbackValue(value, targetType, ex);
            }
        }

        protected object ReturnFallbackValue(object value, Type targetType)
        {
            return ReturnFallbackValue(value, targetType, null);
        }
        protected object ReturnFallbackValue(object value, Type targetType, Exception ex)
        {
            if (FallbackBehavior == FallbackBehavior.ReturnFallbackValue)
                return FallbackValue;

            if (FallbackBehavior == FallbackBehavior.ReturnOriginalValue)
                return value;

            if (FallbackBehavior == FallbackBehavior.ReturnUnsetValue)
                return DependencyProperty.UnsetValue;

            throw new ConverterException(string.Format("Converter {0}: unable to convert value from {1} to {2}",
                GetType().FullName, value == null ? "null" : value.GetType().FullName, targetType.FullName), ex);
        }

        protected virtual bool GetReversedValue(object value, Type targetType, out object reversedValue)
        {
            // boolean
            if (value is bool)
            {
                reversedValue = !(bool)value;
                return true;
            }

            reversedValue = null;
            return false;
        }

        protected abstract object TryConvert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture);
        protected abstract object TryConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture);

        protected object ReturnValue(object value, Type targetType)
        {
            if (IsReversed)
            {
                object reversedValue;
                if (GetReversedValue(value, targetType, out reversedValue))
                    return reversedValue;

                ReturnFallbackValue(value, targetType);
            }

            return value;
        }

        protected virtual object GetDefaultValue(Type type)
        {
            if (type.IsValueType)
                return Activator.CreateInstance(type);

            return null;
        }
    }
}
